#include <gtest/gtest.h>

#include "trace.h"

using namespace std;

Trace create_trace1()
{
  Trace trace;
  trace.set_dimension(1);

  // set IDs
  vector<int> ids;
  ids.push_back(1);
  trace.set_ids(ids);

  vector<double> values;
  values.push_back(0);

  // add 3 items
  Trace_item *trace_item;
  trace_item = new Trace_item;
  values[0] = 8.0;
  trace_item->set_values(values);
  trace_item->set_time(0.0);
  trace.add_item(trace_item);
  trace_item = new Trace_item;
  values[0] = 10.0;
  trace_item->set_values(values);
  trace_item->set_time(1.0);
  trace.add_item(trace_item);
  trace_item = new Trace_item;
  values[0] = 15.0;
  trace_item->set_values(values);
  trace_item->set_time(2.0);
  trace.add_item(trace_item);

  return trace;
}

Trace create_trace2()
{
  Trace trace;
  trace.set_dimension(2);

  // set IDs
  vector<int> ids;
  ids.push_back(1);
  ids.push_back(4);
  trace.set_ids(ids);

  vector<double> values;
  values.push_back(0);
  values.push_back(2);

  // add 2 items
  Trace_item *trace_item;
  trace_item = new Trace_item;
  values[0] = 8.0;
  trace_item->set_values(values);
  trace_item->set_time(0.0);
  trace.add_item(trace_item);
  trace_item = new Trace_item;
  values[0] = 10.0;
  trace_item->set_values(values);
  trace_item->set_time(1.0);
  trace.add_item(trace_item);

  return trace;
}

TEST(Trace, trim)
{
  Trace trace = create_trace1();

  ASSERT_EQ(1, trace.get_dimension());
  ASSERT_EQ(3, trace.size());

  trace.trim_beginning(0.5);
  ASSERT_EQ(3, trace.size());
  ASSERT_EQ(0.5, trace.start_time().value);
  ASSERT_NEAR(9.0, trace.get_value(1,0.5), 1e-8);

  trace = create_trace1();

  trace.trim_end(0.9);
  ASSERT_EQ(2, trace.size());

  ASSERT_EQ(0.9, trace.end_time().value);
  ASSERT_NEAR(9.8, trace.get_value(1,0.9), 1e-8);

  trace = create_trace1();
  trace.trim_beginning(1.9);
}

TEST(Trace, contains)
{
  Trace trace = create_trace1();

  ASSERT_EQ(1, trace.get_dimension());
  ASSERT_EQ(3, trace.size());

  ASSERT_TRUE(trace.contains(0.0));
  ASSERT_TRUE(trace.contains(0.4));
  ASSERT_TRUE(trace.contains(1.7));
  ASSERT_TRUE(trace.contains(2.0));
  ASSERT_FALSE(trace.contains(-0.1));
  ASSERT_FALSE(trace.contains(2.1));
}

TEST(Trace, interpolate)
{
  Trace trace = create_trace1();

  ASSERT_NEAR(9.0, trace.get_value(1,0.5), 1e-8);
  ASSERT_NEAR(10.0, trace.get_value(1,1.0), 1e-8);
  ASSERT_NEAR(11.0, trace.get_value(1,1.2), 1e-8);
  ASSERT_LT(11.0, trace.get_value(1,1.3));
}

TEST(Trace, save)
{
  Trace trace;
  trace.set_dimension(2);

  // set IDs
  vector<int> ids;
  ids.push_back(0);
  ids.push_back(1);
  trace.set_ids(ids);

  // create trace item
  Trace_item *trace_item = new Trace_item;
  vector<double> values;
  values.push_back(34);
  values.push_back(8);
  trace_item->set_values(values);
  trace_item->set_time(1.0);
  trace.add_item(trace_item);

  // create trace item
  trace_item = new Trace_item;
  values.clear();
  values.push_back(3);
  values.push_back(65.4);
  trace_item->set_values(values);
  trace_item->set_time(2.0);
  trace.add_item(trace_item);

  trace.save("test_data/trace");
}

TEST(Trace, load)
{
  Trace trace;
  trace.load("trace");
}

TEST(Trace, split1)
{
  Trace trace1 = create_trace1();

  Trace trace2 = trace1.split(1.5);

  // test the old, shorter trace
  ASSERT_NEAR(12.5, trace1.get_value(1,1.5), 1e-8);
  ASSERT_EQ(3, trace1.size());
  ASSERT_NEAR(0.0, trace1.start_time().value, 1e-8);
  ASSERT_NEAR(1.5, trace1.end_time().value, 1e-8);

  // test the new trace
  ASSERT_NEAR(12.5, trace2.get_value(1,1.5), 1e-8);
  ASSERT_NEAR(1.5, trace2.start_time().value, 1e-8);
  ASSERT_NEAR(2.0, trace2.end_time().value, 1e-8);
  ASSERT_EQ(1, trace2.get_dimension());
  ASSERT_EQ(2, trace2.size());
  ASSERT_TRUE(trace2.contains(2.0));
  ASSERT_TRUE(trace2.contains(1.5));
  ASSERT_FALSE(trace2.contains(1.0));

}

TEST(Trace, split2)
{
  Trace trace1 = create_trace1();

  Trace trace2 = trace1.split(0.1, 1.5);

  // test the old, shorter trace
  ASSERT_NEAR(8.0, trace1.get_value(1,0), 1e-8);
  ASSERT_NEAR(8.2, trace1.get_value(1,0.1), 1e-8);
  ASSERT_EQ(2, trace1.size());
  ASSERT_NEAR(0.0, trace1.start_time().value, 1e-8);
  ASSERT_NEAR(0.1, trace1.end_time().value, 1e-8);

  // test the new trace
  ASSERT_NEAR(12.5, trace2.get_value(1,1.5), 1e-8);
  ASSERT_NEAR(1.5, trace2.start_time().value, 1e-8);
  ASSERT_NEAR(2.0, trace2.end_time().value, 1e-8);
  ASSERT_EQ(1, trace2.get_dimension());
  ASSERT_EQ(2, trace2.size());
  ASSERT_TRUE(trace2.contains(2.0));
  ASSERT_TRUE(trace2.contains(1.5));
  ASSERT_FALSE(trace2.contains(1.0));

}

TEST(Trace, drop_ids)
{
  Trace trace = create_trace2();

  ASSERT_EQ(2, trace.get_dimension());

  trace.drop_id(1);

  ASSERT_EQ(1, trace.get_dimension());

  ASSERT_NEAR(2.0, trace.get_value(4,0.5), 1e-8);
}
